
/**
 * 计时器的各个状态
 * 
 * 未启动状态    status=0  baseTime=0  delta=0;
 * 播放状态      status=1  baseTime>0  delta>0;
 * 暂停状态      status=2  baseTime>0  delta>0;
 * 结束状态      status=3  baseTime>0  delta>0;
 * 
 * 未启动状态 -> 播放状态   play();
 * 播放状态 -> 暂停状态     pause();
 * 播放状态 -> 停止状态     stop();
 * 暂停状态 -> 停止状态     stop();
 * 暂停状态 -> 播放状态     play() / undoPause();
 * 停止状态 -> 播放状态     play() / undoPause();
 * [任何]状态 -> 未启动状态 reset();
 * 其余状态的过渡都是不允许的. 除非带参数播放 / 暂停 / 停止
 */
export default class Timer {

  static STATUS_INIT = 0;
  static STATUS_PLAYING = 1;
  static STATUS_PAUSED = 2;
  static STATUS_FINISHED = 3;

  /**
   * 未启动状态  status=0
   * 播放状态    status=1
   * 暂停状态    status=2
   * 结束状态    status=3
   */
  status = Timer.STATUS_INIT;

  /**
   * 基准时间 timestamp, 非负数
   */
  baseTime = 0;

  /**
   * 计算现在计时器的值, 单位毫秒, 非负数
   */
  delta = 0;

  // 其它时间显示数字, 均为非负数
  ds = 0; // 1/10 秒
  s = 0;
  m = 0;
  h = 0;

  /**
   * 状态发生变化的回调函数, 变化时通知, 参数是 this
   */
  listener = () => {}

  reset() {
    this.status = Timer.STATUS_INIT;
    this.baseTime = 0;
    this.delta = 0;

    this.ds = 0;
    this.s = 0;
    this.m = 0;
    this.h = 0;
    this.callListener();
  }

  /**
   * @param {number | null} fromTime 可以设置当前计时器示数, 单位毫秒
   * @returns {number} 当前计时器状态
   */
  play(fromTime = null) {
    this.status = Timer.STATUS_PLAYING;
    const now = Date.now();
    if (fromTime != null && fromTime > 0) {
      this.delta = fromTime | 0;
    }
    this.baseTime = now - this.delta;
    this.callListener();
    return this.status;
  }

  /**
   * 只有当前的状态是 playing, 暂停才有效
   * @param {number | null} toTime 可以设置当前计时器示数, 单位毫秒
   * @returns {number} 当前计时器状态
   */
  pause(toTime = null) {
    this._doPause(toTime, Timer.STATUS_PAUSED);
    return this.status;
  }

  /**
   * @param {number} toTime 设置当前计时器示数, 单位毫秒
   * @param {number} toStatus 转变的状态
   */
  _doPause(toTime, toStatus) {
    if (this.status != Timer.STATUS_PLAYING) {
      return this.status;
    }
    if (toTime == null || typeof(toTime) != 'number') {
      const now = Date.now();
      toTime = now - this.baseTime;
    }
    this.status = toStatus;
    this.delta = toTime;
    this.calc();
    this.callListener();
  }

  /**
   * @param {number | null} toTime 可以设置当前计时器示数, 单位毫秒
   * @returns {number} 当前计时器状态
   */
  stop(toTime = null) {
    if (this.status == Timer.STATUS_PAUSED) {
      this.status = Timer.STATUS_FINISHED;
      if (toTime != null && toTime > 0) {
        this.delta = toTime;
      }
      this.calc();
      this.callListener();
      return this.status;
    }
    this._doPause(toTime, Timer.STATUS_FINISHED);
    return this.status;
  }

  undoPause() {
    if (this.baseTime == 0) {
      // 就是没暂停过, 不操作
      return;
    }
    this.status = Timer.STATUS_PLAYING;
    const now = Date.now();
    this.delta = now - this.baseTime;
    this.calc();
    this.callListener();
    return this.status;
  }

  /**
   * @param {number | null} now 可以传入当前的时间戳
   */
  tick(now = null) {
    if (this.status !== Timer.STATUS_PLAYING) {
      return;
    }
    const time = now != null ? now : Date.now();
    this.delta = time - this.baseTime;
    this.calc();
  }

  /**
   * 计算 ds、s、m、h 的值
   */
  calc() {
    const delta = (this.delta / 100) | 0;
    this.ds = parseInt(delta % 10);
    this.s = parseInt((delta % 600) / 10);

    // minute
    if (delta >= 600) {
      this.m = parseInt((delta % 36000) / 600);
    } else {
      this.m = 0;
    }

    // hour
    if (delta >= 36000) {
      this.h = parseInt(delta / 36000);
    } else {
      this.h = 0;
    }
  }

  callListener() {
    this.listener(this);
  }

  setListener(l) {
    this.listener = l ? l : () => {};
  }

  isInit() { return this.status === Timer.STATUS_INIT; }
  isPlaying() { return this.status === Timer.STATUS_PLAYING; }
  isPaused() { return this.status === Timer.STATUS_PAUSED; }
  isFinished() { return this.status === Timer.STATUS_FINISHED; }

  started() {
    return !this.isInit();
  }
}